/*
*  eventproc.c - Process events
*  Copyright (c) 2005 Derek Ingrouville (dergro@users.sourceforge.net)
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Library General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <stdlib.h>

#include "eventproc.h"
#include "gdialog.h"
#include "msn/connection.h"

#define DISCONNECTED 0x02

int process_msn_data (MsnConnection *conn);
int process_event_queue (MsnConnection *conn);

gboolean
on_data_available (GIOChannel *source, 
		   GIOCondition condition, 
		   MsnConnection *conn)
{
	g_return_val_if_fail (source != NULL, FALSE);
	g_return_val_if_fail (conn != NULL, FALSE);

	switch (condition) {
		case G_IO_IN: {
			g_assert (conn->connected);
			process_msn_data (conn);
			process_event_queue (conn);
			break;
		}
		case G_IO_HUP: {
			on_disconnected (conn);
			break;
		}
		default: break;
	}

	return TRUE;
}

int
process_msn_data (MsnConnection *conn)
{
	MsnCommand *cmd = NULL;
	MsnCallback func = NULL;
	int ret = 0;

	g_return_val_if_fail (conn != NULL, 0);
	g_return_val_if_fail (conn->callbacks != NULL, 0);

	cmd = msn_connection_recv_cmd (conn);
	if (cmd == NULL)
		return 0;

	g_print ("Received: %s\n{\n", cmd->name);
	g_print ("Processing Command %s\n", cmd->name);

	func = msn_callbacks_lookup (conn->callbacks, cmd->name);
	if (func == NULL) {
		/* Unhandled Command */
		g_message ("Unhandled Command Recieved: %s\r\n", cmd->name);
		return 0;
	}

	ret = func (conn->callbacks->cmd_data, cmd);

	g_print ("}\n\n");

	/* TODO: crash on command_free when ret != =0 */
	if (ret == 0)
		msn_command_free (cmd);

	if (ret != 0) {
		g_printf ("cmd->command = %s\n", cmd->command);
	}
	g_assert (ret == 0);

	return ret;
}

int
process_event_queue (MsnConnection *conn)
{
	int (*func)(gpointer data, gpointer user_data);
	MsnEvent *event = NULL;
	int ret = -1;

	g_return_val_if_fail (conn != NULL, 0);
	g_return_val_if_fail (conn->events != NULL, 0);

	while ( (event = msn_get_event (conn->events)) != NULL) {
		g_print ("Processing event %d: %s\n{\n", event->id, event->name);

		func = msn_callbacks_lookup (conn->callbacks, event->name);
		if (func == NULL) {
			/* Unhandled Command */
			g_message ("Unhandled event: %s\r\n", event->name);
			return 0;
		}

		ret = func (conn->callbacks->event_data, event);
		if (ret == DISCONNECTED) {
			/* Disconnected by the previous handler */
			g_warning ("event: %s: callback returned DISCONNECTED\n", 
				  event->name);
			break;
		}

		g_print ("}\n\n");
		msn_event_free (event);
	}

	return ret;
}

void
on_connection_established (MsnConnection *conn)
{
	g_return_if_fail (conn != NULL);

	g_io_add_watch (conn->channel, 
		G_IO_IN,
		on_data_available,
		conn);

	g_print ("connection established\n");

	return;
}

void
on_disconnected (MsnConnection *conn)
{
	g_return_if_fail (conn != NULL);
	g_print ("connection closed\n");
	return;
}

int
on_group_added (GMainWindow *gmain, MsnEvent *event)
{
	MsnGroup *group = NULL;
	int ret = 0;

	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (gmain->active->gclist != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);

	group = (MsnGroup *) event->user_data;
	g_return_val_if_fail (group != NULL, -1);

	ret = gclist_add_group (gmain->active->gclist, group);

	g_print ("Group Added: %s\r\n", group->name);

	return ret;
}

int
on_group_info_changed (GMainWindow *gmain, MsnEvent *event)
{	
	MsnGroup *group = NULL;
	int ret = 0;

	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (gmain->active->gclist != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);

	group = (MsnGroup *) event->user_data;
	g_return_val_if_fail (group != NULL, -1);

	ret = gclist_update_group (gmain->active->gclist, group);

	g_print ("Group Info Changed: %s\r\n", group->name);

	return ret;
}

int
on_group_removed (GMainWindow *gmain, MsnEvent *event)
{
	int *p_group_id;
	int group_id = 0;
	int ret = 0;

	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (gmain->active->gclist != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);

	p_group_id = event->user_data;
	g_return_val_if_fail (p_group_id != NULL, -1);

	group_id = *p_group_id;

	g_free (p_group_id);

	ret = gclist_remove_group (gmain->active->gclist, group_id);

	return ret;
}

int
on_contact_added (GMainWindow *gmain, MsnEvent *event)
{
	MsnContact *contact = NULL;
	int ret = 0;

	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (gmain->active->gclist != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);

	contact = (MsnContact *) event->user_data;
	g_return_val_if_fail (contact != NULL, -1);

	ret = gclist_add_contact (gmain->active->gclist, contact);

	g_print ("Contact Added: %s\r\n", contact->passport);

	return ret;
}

int
on_contact_info_changed (GMainWindow *gmain, MsnEvent *event)
{
	MsnContact *contact = NULL;
	GConvo *convo = NULL;
	int ret = 0;

	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (gmain->active->gclist != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);

	contact = (MsnContact *) event->user_data;
	g_return_val_if_fail (contact != NULL, -1);

	ret = gclist_update_contact (gmain->active->gclist, contact);

	/* Check if theres any conversations that need to be updated */
	convo = gmain_window_find_convo_with_participant (gmain, contact->passport);
	if (convo != NULL)
		gconvo_refresh_info (convo);

	g_print ("Contact Info Changed: %s\r\n", contact->passport);
	g_print ("contact->status: %i\r\n", contact->status);

	return ret;
}

int
on_user_nickname_changed (GMainWindow *gmain, MsnEvent *event)
{
	char *nickname = NULL;

	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);

	nickname = (char *) event->user_data;
	g_return_val_if_fail (nickname != NULL, -1);

	/* account has already been updated with the new nickname */
	gmain_window_update_nickname (gmain);

	g_print ("Nickname changed to: %s\n", nickname);

	g_free (nickname);

	return 0;
}

int
on_user_status_changed (GMainWindow *gmain, MsnEvent *event)
{

	char *str = NULL;
	MsnAccount *acct = NULL;

	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);

	acct = (MsnAccount *) event->user_data;
	g_return_val_if_fail (acct != NULL, -1);

	str = get_string_from_status (acct->status);
	g_assert (str != NULL);

	/* Update the nickname label to reflect new status */
	gmain_window_update_user (gmain);

	g_print ("Status changed to: %s\n", str);

	g_free (str);

	return 0;
}

int
on_sync_finished (GMainWindow *gmain, MsnEvent *event)
{
	MsnSession *session = NULL;

	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);

	session = (MsnSession *) event->user_data;

	gtk_tree_view_expand_all (gmain->active->gclist->view);

	/* msn_set_status (session, ONLINE); */
	msn_set_status (session, HIDDEN);

	return 0;
}

int
on_switchboard_invited (GMainWindow *gmain, MsnEvent *event)
{
	GConvo *convo = NULL;
	MsnSwitchboard *sboard = NULL;
	MsnCommand *cmd = NULL;
	char **ip_port = NULL;
	int port = 0;
	unsigned int id = 0;
	int ret = 0;

	/* TODO: possible helper function? relate to gclist clicked */

	/* RNG SESSION_ID IP:PORT CKI AUTHSTR passport screenname */     

	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);
	g_return_val_if_fail (event->user_data != NULL, -1);

	cmd = (MsnCommand *) event->user_data;

	/* Check if we already have a gconvo available */
	convo = gmain_window_find_convo_with_participant (gmain, 
		cmd->params [4]);

	if (convo == NULL) {
		convo = gconvo_new (gmain->active->session, SWITCHBOARD_CHAT_EXISTING);
		gconvo_set_window_closed_cb (convo, gmain_window_convo_closed, gmain);
		gmain_window_add_convo (gmain, convo);
	}

	id = atoi (cmd->params [0]);
	ip_port = g_strsplit (cmd->params [1], ":", 2);
	port = atoi (ip_port[1]);

	if (convo->sboard == NULL) {
		sboard = msn_switchboard_new (gmain->active->session, id, SWITCHBOARD_CHAT_EXISTING);
		gconvo_set_switchboard (convo, sboard);
	}

	if (convo->connected == FALSE)
		ret = msn_switchboard_join_session (sboard, ip_port [0], port, cmd->params [3]);
	else {
		/* TODO: Look into this, check what happens on file invites and others */
		g_warning ("Recieved invitation to an existing convo, %s\n",
			"possibly unimplemented feature, you shouldn't be seeing this");
	}

	msn_command_free (cmd);

	gconvo_show (convo);

	return ret;
}

int
on_signed_on_elsewhere (GMainWindow *gmain, MsnEvent *event)
{
	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);

	vobri_display_message (NULL, "You have signed on from another location");
	gmain_window_reset (gmain);

	return DISCONNECTED;
}

int
on_server_maintainence (GMainWindow *gmain, MsnEvent *event)
{
	g_return_val_if_fail (gmain != NULL, -1);
	g_return_val_if_fail (event != NULL, -1);

	vobri_display_message (NULL, 
		"Server going down for maintenance");

	gmain_window_reset (gmain);

	return DISCONNECTED;
}

void
setup_event_handlers (MsnCallbacks *cbs)
{
	g_return_if_fail (cbs != NULL);

	msn_callbacks_add_event (cbs, "group_added", on_group_added);
	msn_callbacks_add_event (cbs, "group_info_changed", on_group_info_changed);
	msn_callbacks_add_event (cbs, "group_removed", on_group_removed);
	msn_callbacks_add_event (cbs, "contact_added", on_contact_added);
	msn_callbacks_add_event (cbs, "contact_info_changed", on_contact_info_changed);
	msn_callbacks_add_event (cbs, "user_nickname_changed", on_user_nickname_changed);
	msn_callbacks_add_event (cbs, "user_status_changed", on_user_status_changed);
	msn_callbacks_add_event (cbs, "sync_finished", on_sync_finished);
	msn_callbacks_add_event (cbs, "switchboard_invited", on_switchboard_invited);
	msn_callbacks_add_event (cbs, "signed_on_elsewhere", on_signed_on_elsewhere);
	msn_callbacks_add_event (cbs, "server_maintainence", on_server_maintainence);
}
